@@ -170,6 +170,7 @@ def upload_temperature(request): |
||
170 | 170 |
try: |
171 | 171 |
eqpt = ThermometerEquipmentInfo.objects.get(macid=macid, status=True) |
172 | 172 |
except ThermometerEquipmentInfo.DoesNotExist: |
173 |
+ ThermometerMeasureLogInfo.objects.create(point_id=eqpt.point_id, macid=macid, name=name, sex=sex, birth_stamp=birth_stamp, phone=phone, start_stamp=start_stamp, end_stamp=end_stamp, temperature=temperature, upload_temperature_info=request.POST, status=False) |
|
173 | 174 |
return response(ThermometerEquipmentStatusCode.THERMOMETER_EQUIPMENT_NOT_FOUND) |
174 | 175 |
|
175 | 176 |
try: |
@@ -196,3 +197,65 @@ def upload_temperature(request): |
||
196 | 197 |
}) |
197 | 198 |
|
198 | 199 |
return response() |
200 |
+ |
|
201 |
+ |
|
202 |
+def mqtt_upload_temperature(payload): |
|
203 |
+ # Received `{"mac":"A4DA324E7A63","pkt":"215","chg_sta":true,"bat":"100","raw_temp":"4697,4696,4697","sta":"0","alg_temp":"4697,4696,4697","alg_gstr":0,"ble_rssi":-21,"wifi_rssi":-68,"current_time":"2021-08-08 15:22:59"}` from `esp/240AC4D3C1AC` topic |
|
204 |
+ # |
|
205 |
+ # { |
|
206 |
+ # "mac": "A4DA324E7A63", # 体温贴 mac,固定 6 个字节,12 个字符 |
|
207 |
+ # "pkt": "215", # 广播包包序为 85,有效包序范围[1, 255] |
|
208 |
+ # "chg_sta": true, # 充电状态,true 充电,false 未充电 |
|
209 |
+ # "bat": "100", # 电量剩余 65%,有效电量范围[0, 100] |
|
210 |
+ # "raw_temp": "4697,4696,4697", # 三个原始温度数 |
|
211 |
+ # "sta": "0", # 算法返回状态 |
|
212 |
+ # "alg_temp": "4697,4696,4697", # 算法返回三个温度 |
|
213 |
+ # "alg_gstr": 0, # 算法手臂姿态 |
|
214 |
+ # "ble_rssi": -21, # 体温贴相对于底座的信号强度 |
|
215 |
+ # "wifi_rssi": -68, # 底座网络信号强度 |
|
216 |
+ # "current_time": "2021-08-08 15:22:59" # 底座接收到体温贴信息的实时时间 |
|
217 |
+ # } |
|
218 |
+ try: |
|
219 |
+ payload = json.loads(payload) |
|
220 |
+ except Exception: |
|
221 |
+ return |
|
222 |
+ |
|
223 |
+ macid = payload.get('mac', '') |
|
224 |
+ macid = f'{macid[:2]}:{macid[2:4]}:{macid[4:6]}:{macid[6:8]}:{macid[8:10]}:{macid[10:12]}' |
|
225 |
+ current_time = payload.get('current_time', '') |
|
226 |
+ start_stamp = end_stamp = tc.string_to_timestamp(current_time) |
|
227 |
+ # raw_temp = payload.get('raw_temp', '') |
|
228 |
+ alg_temp = payload.get('alg_temp', '') |
|
229 |
+ |
|
230 |
+ # temp = raw_temp.split(',') + alg_temp.split(',') |
|
231 |
+ temp = alg_temp.split(',') |
|
232 |
+ temp = [int(t) for t in temp if t] |
|
233 |
+ |
|
234 |
+ if not temp: |
|
235 |
+ return |
|
236 |
+ |
|
237 |
+ temperature = max(temp) / 100 |
|
238 |
+ |
|
239 |
+ try: |
|
240 |
+ eqpt = ThermometerEquipmentInfo.objects.get(macid=macid, status=True) |
|
241 |
+ except ThermometerEquipmentInfo.DoesNotExist: |
|
242 |
+ ThermometerMeasureLogInfo.objects.create(macid=macid, start_stamp=start_stamp, end_stamp=end_stamp, temperature=temperature, temperature_src=ThermometerMeasureLogInfo.MQTT, upload_temperature_info=payload, status=False) |
|
243 |
+ return |
|
244 |
+ |
|
245 |
+ try: |
|
246 |
+ point = IsolationPointInfo.objects.get(point_id=eqpt.point_id, status=True) |
|
247 |
+ except IsolationPointInfo.DoesNotExist: |
|
248 |
+ return |
|
249 |
+ |
|
250 |
+ point_measure_ymd = tc.local_string(format='%Y-%m-%d') |
|
251 |
+ point_measure_window = point.current_measure_window |
|
252 |
+ |
|
253 |
+ eqpt.last_submit_at = tc.utc_datetime() |
|
254 |
+ eqpt.save() |
|
255 |
+ |
|
256 |
+ ThermometerMeasureLogInfo.objects.create(point_id=eqpt.point_id, macid=macid, start_stamp=start_stamp, end_stamp=end_stamp, temperature=temperature, temperature_src=ThermometerMeasureLogInfo.MQTT, upload_temperature_info=payload) |
|
257 |
+ |
|
258 |
+ if point_measure_window: |
|
259 |
+ ThermometerMeasureInfo.objects.update_or_create(point_id=eqpt.point_id, point_measure_ymd=point_measure_ymd, point_measure_window=point_measure_window, macid=macid, defaults={ |
|
260 |
+ 'temperature': temperature, |
|
261 |
+ }) |
@@ -0,0 +1,106 @@ |
||
1 |
+# -*- coding: utf-8 -*- |
|
2 |
+ |
|
3 |
+import logging |
|
4 |
+import random |
|
5 |
+import time |
|
6 |
+ |
|
7 |
+from django_six import CompatibilityBaseCommand |
|
8 |
+from paho.mqtt import client as mqtt_client |
|
9 |
+from api.eqpt_views import mqtt_upload_temperature |
|
10 |
+ |
|
11 |
+ |
|
12 |
+logger = logging.getLogger('console') |
|
13 |
+ |
|
14 |
+ |
|
15 |
+# defined(PRORON_MQTT_DOMESTIC_SERVER) |
|
16 |
+# define MQTT_SERVER_TYPE MQTT_SERVER_TYPE_DOMAIN_NAME |
|
17 |
+# define MQTT_BROKER_URI "china.mqtt.protontek.com" /* Domestic server "47.100.92.19" */ |
|
18 |
+# define MQTT_USERNAME "proton" |
|
19 |
+# define MQTT_PASSWORD "proton123" |
|
20 |
+# define MQTT_PORT 1883 |
|
21 |
+broker = 'china.mqtt.protontek.com' |
|
22 |
+username = 'proton' |
|
23 |
+password = 'proton123' |
|
24 |
+port = 1883 |
|
25 |
+topic = 'esp/#' |
|
26 |
+client_id = f'python-mqtt-{random.randint(0, 1000)}' |
|
27 |
+ |
|
28 |
+ |
|
29 |
+# MQTT连接 |
|
30 |
+def connect_mqtt(): |
|
31 |
+ def on_connect(client, userdata, flags, rc): |
|
32 |
+ if rc == 0: |
|
33 |
+ print('Connected to MQTT Broker') |
|
34 |
+ else: |
|
35 |
+ print('Failed to connect, return code %d\n', rc) |
|
36 |
+ # Set Connecting Client ID |
|
37 |
+ print(f'Connected to MQTT Broker by client_id `{client_id}`') |
|
38 |
+ client = mqtt_client.Client(client_id) |
|
39 |
+ client.username_pw_set(username, password=password) |
|
40 |
+ client.on_connect = on_connect |
|
41 |
+ client.connect(broker, port) |
|
42 |
+ return client |
|
43 |
+ |
|
44 |
+ |
|
45 |
+# MQTT发布消息 |
|
46 |
+# from commands.management.commands.mqtt import publish_run |
|
47 |
+# publish_run() |
|
48 |
+def publish(client): |
|
49 |
+ msg_count = 0 |
|
50 |
+ while True: |
|
51 |
+ time.sleep(1) |
|
52 |
+ msg = f'messages: `{msg_count}`' |
|
53 |
+ result = client.publish(topic, msg) |
|
54 |
+ # result: [0, 1] |
|
55 |
+ status = result[0] |
|
56 |
+ if status == 0: |
|
57 |
+ print(f'Send `{msg}` to topic `{topic}`') |
|
58 |
+ else: |
|
59 |
+ print(f'Failed to send message to topic {topic}') |
|
60 |
+ msg_count += 1 |
|
61 |
+ |
|
62 |
+ |
|
63 |
+def publish_run(): |
|
64 |
+ client = connect_mqtt() |
|
65 |
+ client.loop_start() |
|
66 |
+ publish(client) |
|
67 |
+ |
|
68 |
+ |
|
69 |
+# MQTT订阅消息 |
|
70 |
+def subscribe(client: mqtt_client): |
|
71 |
+ def on_message(client, userdata, msg): |
|
72 |
+ # Received `{"mac":"A4DA324E7A63","pkt":"215","chg_sta":true,"bat":"100","raw_temp":"4697,4696,4697","sta":"0","alg_temp":"4697,4696,4697","alg_gstr":0,"ble_rssi":-21,"wifi_rssi":-68,"current_time":"2021-08-08 15:22:59"}` from `esp/240AC4D3C1AC` topic |
|
73 |
+ # |
|
74 |
+ # { |
|
75 |
+ # "mac": "A4DA324E7A63", # 体温贴 mac,固定 6 个字节,12 个字符 |
|
76 |
+ # "pkt": "215", # 广播包包序为 85,有效包序范围[1, 255] |
|
77 |
+ # "chg_sta": true, # 充电状态,true 充电,false 未充电 |
|
78 |
+ # "bat": "100", # 电量剩余 65%,有效电量范围[0, 100] |
|
79 |
+ # "raw_temp": "4697,4696,4697", # 三个原始温度数 |
|
80 |
+ # "sta": "0", # 算法返回状态 |
|
81 |
+ # "alg_temp": "4697,4696,4697", # 算法返回三个温度 |
|
82 |
+ # "alg_gstr": 0, # 算法手臂姿态 |
|
83 |
+ # "ble_rssi": -21, # 体温贴相对于底座的信号强度 |
|
84 |
+ # "wifi_rssi": -68, # 底座网络信号强度 |
|
85 |
+ # "current_time": "2021-08-08 15:22:59" # 底座接收到体温贴信息的实时时间 |
|
86 |
+ # } |
|
87 |
+ payload = msg.payload.decode() |
|
88 |
+ print(f'Received `{payload}` from `{msg.topic}` topic') |
|
89 |
+ mqtt_upload_temperature(payload) |
|
90 |
+ |
|
91 |
+ client.subscribe(topic) |
|
92 |
+ client.on_message = on_message |
|
93 |
+ |
|
94 |
+ |
|
95 |
+def subscribe_run(): |
|
96 |
+ client = connect_mqtt() |
|
97 |
+ subscribe(client) |
|
98 |
+ client.loop_forever() |
|
99 |
+ |
|
100 |
+ |
|
101 |
+class Command(CompatibilityBaseCommand): |
|
102 |
+ def handle(self, *args, **options): |
|
103 |
+ |
|
104 |
+ logger.info('MQTT client is dealing') |
|
105 |
+ |
|
106 |
+ subscribe_run() |
@@ -22,8 +22,8 @@ class ThermometerMeasureInfoAdmin(ReadOnlyModelAdmin, admin.ModelAdmin): |
||
22 | 22 |
|
23 | 23 |
|
24 | 24 |
class ThermometerMeasureLogInfoAdmin(ReadOnlyModelAdmin, admin.ModelAdmin): |
25 |
- list_display = ('point_id', 'macid', 'sn', 'name', 'sex', 'birth_stamp', 'phone', 'start_stamp', 'end_stamp', 'temperature', 'status', 'updated_at', 'created_at') |
|
26 |
- list_filter = ('point_id', 'status') |
|
25 |
+ list_display = ('point_id', 'macid', 'sn', 'name', 'sex', 'birth_stamp', 'phone', 'start_stamp', 'end_stamp', 'temperature', 'temperature_src', 'status', 'updated_at', 'created_at') |
|
26 |
+ list_filter = ('point_id', 'temperature_src', 'status') |
|
27 | 27 |
|
28 | 28 |
|
29 | 29 |
admin.site.register(IsolationPointInfo, IsolationPointInfoAdmin) |
@@ -0,0 +1,18 @@ |
||
1 |
+# Generated by Django 3.2.6 on 2021-08-08 11:03 |
|
2 |
+ |
|
3 |
+from django.db import migrations, models |
|
4 |
+ |
|
5 |
+ |
|
6 |
+class Migration(migrations.Migration): |
|
7 |
+ |
|
8 |
+ dependencies = [ |
|
9 |
+ ('equipment', '0004_auto_20210712_0035'), |
|
10 |
+ ] |
|
11 |
+ |
|
12 |
+ operations = [ |
|
13 |
+ migrations.AddField( |
|
14 |
+ model_name='thermometermeasureloginfo', |
|
15 |
+ name='temperature_src', |
|
16 |
+ field=models.IntegerField(choices=[(1, '接口回调'), (2, 'MQTT')], default=1, help_text='用户体温来源', verbose_name='temperature_src'), |
|
17 |
+ ), |
|
18 |
+ ] |
@@ -148,6 +148,14 @@ class ThermometerMeasureInfo(BaseModelMixin): |
||
148 | 148 |
|
149 | 149 |
|
150 | 150 |
class ThermometerMeasureLogInfo(BaseModelMixin): |
151 |
+ CALLBACK = 1 |
|
152 |
+ MQTT = 2 |
|
153 |
+ |
|
154 |
+ TEMPERATURE_SRC_TUPLE = ( |
|
155 |
+ (CALLBACK, '接口回调'), |
|
156 |
+ (MQTT, 'MQTT'), |
|
157 |
+ ) |
|
158 |
+ |
|
151 | 159 |
point_id = models.CharField(_('point_id'), max_length=32, blank=True, null=True, help_text='隔离点唯一标识', db_index=True) |
152 | 160 |
|
153 | 161 |
macid = models.CharField(_('macid'), max_length=32, blank=True, null=True, help_text='设备号') |
@@ -163,6 +171,8 @@ class ThermometerMeasureLogInfo(BaseModelMixin): |
||
163 | 171 |
|
164 | 172 |
temperature = models.FloatField(_('temperature'), default=0, help_text='用户体温') |
165 | 173 |
|
174 |
+ temperature_src = models.IntegerField(_('temperature_src'), choices=TEMPERATURE_SRC_TUPLE, default=CALLBACK, help_text='用户体温来源') |
|
175 |
+ |
|
166 | 176 |
upload_temperature_info = models.TextField(_('upload_temperature_info'), blank=True, null=True, help_text='测温结果上传信息') |
167 | 177 |
|
168 | 178 |
class Meta: |
@@ -2,6 +2,7 @@ StatusCode==1.0.0 |
||
2 | 2 |
furl==2.1.2 |
3 | 3 |
jsonfield==3.1.0 |
4 | 4 |
mysqlclient==2.0.3 |
5 |
+paho-mqtt==1.5.1 |
|
5 | 6 |
pysnippets==1.1.4 |
6 | 7 |
requests==2.25.1 |
7 | 8 |
rlog==0.3 |
@@ -1,3 +1,3 @@ |
||
1 |
-ipdb==0.13.3 |
|
2 |
-ipython==7.18.1 |
|
1 |
+ipdb==0.13.9 |
|
2 |
+ipython==7.26.0 |
|
3 | 3 |
uwsgi==2.0.19.1 |
@@ -1,2 +1,2 @@ |
||
1 |
-isort==5.4.2 |
|
2 |
-pycodestyle==2.6.0 |
|
1 |
+isort==5.9.3 |
|
2 |
+pycodestyle==2.7.0 |
@@ -305,7 +305,7 @@ DJANGO_SHORT_URL_REDIRECT_URL = '' |
||
305 | 305 |
|
306 | 306 |
# Django-We Settings |
307 | 307 |
DJANGO_WE_QUOTE_OR_NOT = True |
308 |
-DJANGO_WE_MODEL_DISPLAY_OR_NOT = True |
|
308 |
+DJANGO_WE_MODEL_DISPLAY_OR_NOT = False |
|
309 | 309 |
# Enable Cookie or not |
310 | 310 |
# DJANGO_WE_BASE_REDIRECT_SET_COOKIE = False |
311 | 311 |
# DJANGO_WE_USERINFO_REDIRECT_SET_COOKIE = True |